Retour à la page d’accueil

Visualisation de données avec ggplot2 - Suite

Lors de cette séance nous allons apprendre à visualiser des jeux de données tabulaires avec ggplot2.

Mise en place

Ouvrez avec Rstudio le fichier Script_ggplot_lesson.R qui est dans le répertoire session2_ggplot/materiel.

Lors de cette séance nous allons utiliser plusieurs packages qui contiennent les fonctions dont nous avons besoin:
- tidyverse qui contient plusieurs packages dont ggplot2
- visdat qui permet une représentation rapide des données
- plotly pour faire des graphiques interactifs

Ces packages sont normalement déjà installés. Pour les importer dans votre session, utilisez la fonction library() :

library(tidyverse)
library(visdat)
library(plotly)

Vous devez aussi vous assurer que le répertoire de travail de R est bien le dossier qui contient le matériel de la formation. Le chemin vers ce dossier va être différent en fonction de votre système opérateur.
Pour spécifier le répertoire de travail de R utilisez la fonction setwd():

setwd("~/Desktop/2024_L3_R/session3_stats_reorganisation/materiel")

Import et vérification des données

La version simplifiée des données est dans le dossier session3_stats_reorganisation/data (burghardt_et_al_2015_expt1.txt). Il s’agit de données pour des phénotypes associés au temps nécessaire à des plantes de différents génotypes pour fleurir dans différentes conditions.

Comme notre répertoire de travail est le dossier materiel , nous devons importer les données ainsi:

# Import des données et chargement dans l'objet expt1 
expt1 <- read_tsv("../data/burghardt_et_al_2015_expt1.txt")
## Rows: 957 Columns: 15
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: "\t"
## chr (6): genotype, background, fluctuation, vernalization, bolt.survival, bolt
## dbl (9): plant_nb, temperature, day.length, days.to.bolt, days.to.flower, ro...
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

Plots! Plots! Plots!

Maintenant que nous avons vérifié la qualité de notre jeu de donnée, nous pouvons générer des graphiques afin d’en apprendre plus sur les données générées par l’expérience.

Nous allons utiliser le package R ggplot2, qui utilise le principe de “grammar or graphics”. Il s’agit des briques du graphique. Cette méthode permettant de combiner et superposer différentes couches de briques:

  • data (données)
  • aesthetics
  • objet géométrique (type de graphique)
  • transformations statistiques
  • ajustements positionnels
  • faceting

Les trois premières briques sont essentielles: data, aesthetics et objet géométrique.

  • data - Vos données
  • aesthetics - Ce que l’on veux montrer sur le graphique et qui dépend des données. Par exemple ce qui est en x et y, la couleur, forme, type de ligne, taille, etc… Pour cela nous utilisons la fonction aes().
  • geom_objects - les objets que nous voulons dans le graphique. Un graphique doit avoir au moins un geom_object, et peut en avoir plusieurs. Par exemple:
    • points (geom_point pour des scatter plots, dot plots)
    • lignes (geom_line pour des tendance, séries chronologiques)

Pour plus d’information sur la création de graphique avec ggplot2, voir cette antisèche très utile.

Faire un graphique avec ggplot2

Commençons avec un boxplot: Dessinons le days.to.flower pour les différents génotypes.

La fonction ggplot() permet de commencer le graphique. Il faut aussi indiquer les données à utiliser:

ggplot(expt1)

Mais cela ne produit qu’un canevas gris!

Il nous manque des briques. En l’occurrence nous devons indiquer quelle aesthetics (c’est la terminologie de ggplot2) nous voulons sur ce canevas gris. Nous devons indiquer quelles sont les variables x et y du boxplot.

ggplot(expt1, aes(x = genotype, y = days.to.flower))

ggplot indique maintenant les variables genotype et days.to.flower de nos données sur les axes x et y du graphique.

Mais toujours pas de graphique. C’est parce que nous pas indiqué à ggplot quelle géométrie nous voulons dessiner sur le canevas. Pour faire un boxplot, nous ajoutons au canevas (littéralement avec un +) geom_boxplot():

ggplot(expt1, aes(genotype, days.to.flower)) +
  geom_boxplot()
## Warning: Removed 83 rows containing non-finite outside the scale range
## (`stat_boxplot()`).

Exercice 1: Pouvez vous dessiner un violin plot? (indice: ?geom_violin)

BONUS Quel autre type de géométrie pouvez vous utilisez avec des données de ce type (catégories sur l’axe x et chiffres sur l’axe y)? Aide: Utilisez cet arbre de décision pour choisir les géométries appropriées.

Ajout de plusieurs couches

Nous allons maintenant ajouter d’autres geom_objects au même graphique. Par exemple, des points pour les valeurs des individus en plus du boxplot:

ggplot(expt1, aes(genotype, rosette.leaf.num)) +
  geom_jitter() +
  geom_boxplot()
## Warning: Removed 95 rows containing non-finite outside the scale range
## (`stat_boxplot()`).
## Warning: Removed 95 rows containing missing values or values outside the scale range
## (`geom_point()`).

Exercice 2: Modifiez le graphique pour que les points soient devant plutôt que derrière les boxplots.

BONUS: Ajouter la géométrie dotplot plutôt que jitter. Utilisez les exemples dans l’aide (avec ?nom de fonction) pour savoir comment l’utiliser, ou lisez cette page de blog

Couleurs!

Changeons la couleur des boxplots:

ggplot(expt1, aes(genotype, days.to.flower)) +
  geom_boxplot(colour = "red")
## Warning: Removed 83 rows containing non-finite outside the scale range
## (`stat_boxplot()`).

Ou la couleur à l’intérieur ( fill) des boxplots:

ggplot(expt1, aes(genotype, days.to.flower)) +
  geom_boxplot(colour = "red", fill = "royalblue")
## Warning: Removed 83 rows containing non-finite outside the scale range
## (`stat_boxplot()`).

Ou même leur transparence:

ggplot(expt1, aes(genotype, days.to.flower)) +
  geom_boxplot(colour = "red", fill = "royalblue", alpha = 0.5)
## Warning: Removed 83 rows containing non-finite outside the scale range
## (`stat_boxplot()`).

C’est sympa, mais cela ne nous apporte pas d’information en plus sur les données.

Par exemple, ajoutons une couleur qui change en fonction d’un des traitements auquel les plantes ont été exposées (par exemple fluctuation). En langage ggplot2, nous voulons lier la valeur de la variable fluctuation à la couleur dans l’ aesthetic du graphique.

Nous devons donc inclure cette information dans aes():

ggplot(expt1, aes(genotype, days.to.flower, colour = fluctuation)) +
  geom_boxplot()
## Warning: Removed 83 rows containing non-finite outside the scale range
## (`stat_boxplot()`).

Wow! ggplot a automatiquement séparé les données de chaque génotype en deux groupes en fonction de fluctuation et leur a attribué une couleur.

Imaginons que nous voulons ajouter les points pour les valeurs des individus, sans couleur, derrière les boxplots colorés:

ggplot(expt1, aes(genotype, days.to.flower, colour = fluctuation)) +
  geom_jitter() +
  geom_boxplot(alpha = 0.5)
## Warning: Removed 83 rows containing non-finite outside the scale range
## (`stat_boxplot()`).
## Warning: Removed 83 rows containing missing values or values outside the scale range
## (`geom_point()`).

Ce n’est pas ce que nous voulons. L’aesthetique colour a été liée à toutes les géométries du graphique. C’est parce qu’elle a été définie dans la fonction ggplot(), qui affecte tous les geom_object qui viennent après.

Mais nous pouvons aussi définir l’aesthetique dans chaque géométrie:

ggplot(expt1, aes(genotype, days.to.flower)) +
  geom_jitter() +
  geom_boxplot(aes(fill = fluctuation), alpha = 0.5)
## Warning: Removed 83 rows containing non-finite outside the scale range
## (`stat_boxplot()`).
## Warning: Removed 83 rows containing missing values or values outside the scale range
## (`geom_point()`).

Exercice 3: Nous voulons étudier la relation entre le nombre de feuille de rosette et la longueur la limbe des feuilles (en mm) pour les génotypes.

Pour cela, dessinez un scatter plot (geom_point()) entre blade.length.mm et rosette.leaf.num en colorant les points en fonction de genotype.

Que se passe t-il si les points sont colorés en fonction de days.to.bolt?

BONUS: Dessinez un scatter plot (geom_point()) entre blade.length.mm et rosette.leaf.num en colorant les points en fonction de vernalization, et changez la couleur des points pour que “NV” soit en bleu et “V” soit en vert. Aide: Trouvez la solution en ligne (par exemple en tapant “change color points ggplot R”)


Facetting

Souvent trop de variables, ou de groupes, sont dans nos données pour pouvoir uniquement utiliser les couleurs pour les discriminer.

C’est le cas par exemple du scatterplot produit dans l’exercice précédent, où les points sont colorés en fonction du génotype. Il est difficile de bien discerner les données pour chaque génotype car ils sont très rapprochés. L’idéal serait d’avoir un scatterplot par génotype.

Ceci est assez facile à faire avecggplot2, il suffit d’ajouter une couche au graphique appelée “facet”:

  • facet_grid() - qui permet d’organiser les panneaux du graphique en lignes et/ou colonnes
  • facet_wrap() - qui permet d’organiser les panneaux du graphique à la suite les uns des autres qui reviennent à la ligne après un certain nombre de panneaux.

Voyons comment facet_grid() fonctionne:

ggplot(expt1, aes(blade.length.mm, rosette.leaf.num, colour = genotype)) +
    geom_point() +
    facet_grid(genotype ~ temperature)
## Warning: Removed 333 rows containing missing values or values outside the scale range
## (`geom_point()`).

Dans facet_grid(), nous utilisons la notation (ligne ~ colonne) pour définir les variables qui sont utilisées pour séparer les panneaux en ligne et colonnes.

Exercice 4: Les couleurs n’apportent plus d’information supplémentaire. Pensez à une utilisation plus intéressante des couleurs dans ce graphique.

Il est possible de ne préciser qu’une variable dans facet_grid() :

# Faceter en ligne
ggplot(expt1, aes(blade.length.mm, rosette.leaf.num, colour = fluctuation)) +
    geom_point() +
    facet_grid(genotype ~ .)
## Warning: Removed 333 rows containing missing values or values outside the scale range
## (`geom_point()`).

# Faceter en colonne
ggplot(expt1, aes(blade.length.mm, rosette.leaf.num, colour = fluctuation)) +
    geom_point() +
    facet_grid(. ~ genotype)
## Warning: Removed 333 rows containing missing values or values outside the scale range
## (`geom_point()`).

Il est aussi possible d’utiliser facet_wrap() si l’on n’utilise qu’une variable pour séparer les données :

ggplot(expt1, aes(blade.length.mm, rosette.leaf.num, colour = fluctuation)) +
    geom_point() +
    facet_wrap( ~ genotype)
## Warning: Removed 333 rows containing missing values or values outside the scale range
## (`geom_point()`).

Exercice 5: Modifiez le graphique précédent afin de séparer les données (facet) en fonction de fluctuation en ligne, et day.length en colonne et de colorer les points en fonction du génotype.

## Warning: Removed 333 rows containing missing values or values outside the scale range
## (`geom_point()`).

En conclusion, il est possible de représenter de nombreuses informations en combinant efficacement facets, couleurs et autres aesthetiques!


Exercice 6: Reproduisez le graphique suivant (ou quelque chose d’approchant): Fig. 2B-C of Burghard et al 2015.

Indice: facet le graphique avec day.length et temperature et colorer l’intérieur des boxplot en fonction de fluctuation.

Pour aller plus loin

Changer l’apparence d’un graphique en utilisant les thèmes

Tous les éléments d’un ggplot sont modifiables. Les thèmes permettent de modifier l’apparence du graphique. Voici quelques exemples.

# Example de thèmes existants dans ggplot2 
ggplot(expt1, aes(genotype, days.to.flower)) +
  geom_boxplot() +
  theme_bw() +
  labs(title = "Black and white theme")

ggplot(expt1, aes(genotype, days.to.flower)) +
  geom_boxplot() +
  theme_classic() +
  labs(title = "Classic theme")

ggplot(expt1, aes(genotype, days.to.flower)) +
  geom_boxplot() +
  theme_minimal() +
  labs(title = "Minimal theme")

La fonction theme() peut être utilisée pour modifier des éléments en particulier du graphique. Il y a tellement de possibilités que le mieux est de rechercher sur internet la modification que vous voulez faire.

Par exemple, en recherchant “vertical labels x axis ggplot2” nous obtenons cette solution:

ggplot(expt1, aes(genotype, days.to.flower)) +
  geom_boxplot() +
  theme(axis.text.x = element_text(angle = 90, hjust = 1))

Rechercher “altering plot colours ggplot2” donne ceci, qui apporte cette solution:

ggplot(expt1, aes(genotype, days.to.flower, fill = fluctuation)) +
  geom_boxplot() +
  scale_fill_brewer(palette="Dark2")

Combiner plusieurs graphiques dans une même figure

Parfois nous voulons crée une figure contenant plusieurs graphiques, c’est le cas dans la plupart des articles scientifiques.

Par exemple le graphique que vous avez reproduit hier correspond aux panels B et C d’une figure de Burghard et al 2015

Nous allons faire quelque chose de similaire avec un scatterplot en panel A, et des boxplots en panel B.

Pour cela nous allons utiliser la fonction ggarrange() qui est dans le package ggpubr.

Tout d’abord, nous devons charger cette librairie dans R:

library(ggpubr)

Ensuite, faisons chaque graphique indépendamment et sauvons les dans un objet:

  • Un scatterplot montrant la relation entre le nombre de feuilles et le nombre de jours jusqu’à floraison, coloré en fonction du traitement:
panelA <- ggplot(expt1, aes(rosette.leaf.num, days.to.bolt, colour = fluctuation)) +
    geom_point() 
  • Des boxplots du nombre de jours jusqu’à floraison pour les différent génotypes, séparés en fonction de la température, et colorés en fonction du traitement (fluctuation ou non):
panelB <- ggplot(expt1, aes(genotype, days.to.bolt, fill = fluctuation)) +
    geom_boxplot() +
    facet_grid(. ~ temperature) +
  theme(axis.text.x = element_text(angle = 90, hjust = 1))

Nous pouvons maintenant utiliser la fonction grid.arrange() pour combiner les graphiques ensemble:

ggarrange(panelA, panelB, nrow=2, labels =c("A", "B"),heights = c(1.5, 2))

Créer des graphiques avec des animations

Si nous postons les graphiques en ligne, cela peut apporter un plus d’inclure une animation dans le graphique, à condition que cela apporte des informations supplémentaires. C’est possible avec le package gganimate!

Tout d’abord, chargeons le package gganimate:

library(gganimate)
## No renderer backend detected. gganimate will default to writing frames to separate files
## Consider installing:
## - the `gifski` package for gif output
## - the `av` package for video output
## and restarting the R session

Pour créer une animation, il faut utiliser la fonction transition_states() à notre graphique (avec un +)

ggplot(expt1, aes(rosette.leaf.num, days.to.bolt, colour = fluctuation)) +
    geom_point()  +
  transition_states(states = genotype, wrap = FALSE, transition_length = 1, state_length = 3) +
  ease_aes('linear') +
  labs(subtitle = "{closest_state}") 
## NULL

L’animation peut ensuite être sauvée en utilisant la fonction anim_save()

Interactivité

Pour aller plus loin, il est même possible de créer des graphiques interactifs en utilisant le package plotly.

library(plotly)

Il faut d’abord mettre le graphique dans un objet, puis utiliser cet objet dans la fonction ggplotly().

# Mettre le graphique dans l'objet p1
p1 <- ggplot(expt1, aes(rosette.leaf.num, days.to.bolt, colour = fluctuation)) + 
  geom_point() +
  facet_wrap(~genotype)

# Utiliser la fonction ggplotly pour faire un graphique intéractif
ggplotly(p1)

Plutôt que d’avoir un graphique par génotype, il est possible d’ajouter un curseur pour montrer les données des génotypes de manière interaction en utilisant l’argument ‘frame’:

# Mettre le graphique dans l'objet p1
p2 <- ggplot(expt1, aes(rosette.leaf.num, days.to.bolt, colour = fluctuation, frame=genotype)) + 
  geom_point() 

# Utiliser la fonction ggplotly pour faire un graphique intéractif
ggplotly(p2)

Extras

D’autres packages qui peuvent apporter un plus à ggplot2:

  • ggridges pour faire des graphiques “ridge”
  • GGally pour représenter automatiquement les relations entre des données